/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package deadlocksim;

import java.util.*;
import deadlocksim.backend.*;
import deadlocksim.backend.io.*;
import deadlocksim.backend.process.*;
import deadlocksim.backend.resources.*;
import deadlocksim.gui.*;
import java.awt.*;
/**
 *
 * @author romilian
 */
public class DeadlockSim 
{
    ResourceManager resourceManager;
    LinkedList<DeadlockProcess> simProcesses;
    SimClock simClock;
    DeadlockStatusNotifier notifier;
    DeadlockResourceDisplay resourceDisplay;
    MainPage mainPage;
    
    boolean isRunning;
    boolean isStepping;
    
    boolean zeroTimeResourceActions;
    
    public DeadlockSim()
    {
        simClock = new SimClock();
        simClock.setSim(this);
        resourceManager = new ResourceManager(this);
        simProcesses = new LinkedList<DeadlockProcess>();
        resourceDisplay = new ButtonResourceDisplay(this);
        ((Component)resourceDisplay).setVisible(true);
        isRunning = false;
        isStepping = false;
        this.setZeroTimeResourceActions(false);
    }
    /*
    public void freeResource(DeadlockProcess p, int resourceTypeID)
    {
        resourceManager.freeResource(p, resourceTypeID);
        RequestResourceAction requestA = new RequestResourceAction(resourceTypeID);
        p.interruptWithAction(requestA);
    }
     * */
    /*
    public void clearSimulator()
    {
        System.out.println("Clearing entire simulator...");
        this.sendMessage("Clearing entire simulator...", false);
        stopClock();
        resetClock();
        resetDisplay();
        //resetResourceManager();
        simClock.deregisterAll();
        simProcesses.clear();
        //resourceManager.clearMaps();
        System.out.println("Simulator cleared. Load new processes/resources.");
        this.sendMessage("Simulator cleared. Load new processes/resources.", false);
    }
    */
    public DeadlockProcess createProcess(Queue<ProcessAction> actionQueue)
    {
        DeadlockProcess newP = new DeadlockProcess(actionQueue, this, simProcesses.size()+1);
        simProcesses.add(newP);
        resourceManager.registerProcess(newP);
        resourceDisplay.addProcess(newP);
        return newP;
    }
    public void destroyProcess(DeadlockProcess p)
    {
        resourceManager.deregisterProcess(p);
        resourceDisplay.destroyProcess(p);
        simProcesses.remove(p);
        
        sendMessage("DELETING process P" + p.getID() + " from simulator!", false);
        refreshDisplay();
    }
    
    public void createResource(int typeID)
    {
        
        int numOfTypeID = resourceManager.getNumOfType(typeID);
        resourceManager.createResource(typeID);
        sendMessage("Creating resource of type " + typeID, false);
        if(numOfTypeID == 0)
        {
            //System.out.println("Adding resource type " + typeID + " to statusDisplay");
            resourceDisplay.addResourceType(typeID);
        }
        refreshDisplay();
    }
    public void destroyResourceType(int resourceTypeID)
    {
        sendMessage("REMOVING ALL resources of type R" + resourceTypeID 
                            +" from simulator!",false);
        this.resourceDisplay.destroyResourceType(resourceTypeID);
        this.resourceManager.destroyResourceType(resourceTypeID);
        refreshDisplay();
        
    }
    public void detectDeadlocks()
    {
        java.util.List<DeadlockProcess> allP = this.getProcesses();
        java.util.List<DeadlockProcess> deadlocked = resourceManager.getDeadlockedProcesses();
        
        Iterator<DeadlockProcess> dIter = allP.iterator();
        
        while(dIter.hasNext())
        {
            DeadlockProcess p = dIter.next();
            if(deadlocked.contains(p))
                p.setDeadlocked(true);
            else
                p.setDeadlocked(false);
        }
        //if(allP.size() == deadlocked.size())
          //  sendMessage("NOTE: All processes are DEADLOCKED!", false);
    }
    
    public int getGlobalTime()
    {
        return simClock.getTime();
    }
    public java.util.List<DeadlockProcess> getProcesses()
    {
        return new LinkedList<DeadlockProcess>(simProcesses);
    }
    
    public DeadlockResourceDisplay getResourceDisplay()
    {
        return resourceDisplay;
    }
    
    public ResourceManager getResourceManager()
    {
        return resourceManager;
    }
    public SimClock getSimClock()
    {
        return simClock;
    }
    
    public void initActiveProcesses()
    {
        simClock.setActiveProcesses();
    }
    
    public void refreshDisplay()
    {
        int globalTime = this.getGlobalTime();
        resourceDisplay.refreshDisplay(globalTime);
        if(mainPage != null)
            mainPage.pack();
    }
    
    public void resetClock()
    {
        simClock.reset();
    }
    public void resetDisplay()
    {
        this.resourceDisplay.reset();
    }
    public void resetProcesses()
    {
        Iterator<DeadlockProcess> iter = simProcesses.iterator();
        while(iter.hasNext())
        {
            iter.next().reset();
        }
    }
    /*public void resetResourceManager()
    {
        // NEED TO IMPLEMENT
        resourceManager.reset();
    }*/
    
    public void resetScenario()
    {
        isStepping = false;
        this.sendMessage("Resetting simulation scenario...", false);
        this.stopSimulation();
        resetClock();
        resetProcesses();
        resourceDisplay.rebuildDisplay();
        this.sendMessage("Reset complete.", false);
    }
    
    public void sendMessage(String message, boolean err)
    {
        notifier.sendNotification(message, err);
    }
    
    public void setNotifier (DeadlockStatusNotifier n){
        notifier = n;
    }
    
    public void setMainFrame(MainPage gui)
    {
        mainPage = gui;
        setNotifier(new TextAreaNotifier(mainPage.getStatusTextArea()));
    }
    
    public void setZeroTimeResourceActions(boolean zeroTime)
    {
        zeroTimeResourceActions = zeroTime;
        if(zeroTime)
            simClock.setDelay(4000);
        else
            simClock.setDelay(2000); // More steps, so make each shorter
    }
    
    public void executeTimestep()
    {
        isStepping = true;
        simClock.clockTick();
    }
    
    public boolean isRunning()
    {
        return isRunning;
    }
    public boolean isStepping()
    {
        return isStepping;
    }
    // USed for pausing the sim during the menu operations
    
    public void runSimulation()
    {
        isRunning = true;
        startClock();
    }
    public void stopSimulation()
    {
        isRunning = false;
        stopClock();
    }
    public void startClock()
    {
        isStepping = false;
        simClock.startTicking();
    }
    
    public void stopClock()
    {
        simClock.stopTicking();
    }
    
    // Returns true if resource Actions (request, free) take no time
    public boolean zeroTimeResourceActions()
    {
        return zeroTimeResourceActions;
    }
}
